' PicBasic Compiler program that reads the matrix keypad on a
' LAB-X1 Experimenter Board and displays the values on the LCD.

' PIC16F877, PBC 1.44, LAB-X1 Experimenter Board

Symbol key = b2			' Holds key/display value
Symbol row = b4			' Holds row number 
Symbol col = b6			' holds column number

Symbol PORTB = $106		' PORTB is register hexadecimal 106
Symbol TRISB = $186		' PORTB data direction is register hexadecimal 186
Symbol  PORTD = 8       ' PORTD is register 8
Symbol  PORTE = 9       ' PORTD is register 9
Symbol  TRISD = $88     ' PORTD data direction is register hexadecimal 88
Symbol  TRISE = $89     ' PORTD data direction is register hexadecimal 89
Symbol	ADCON1 = $9f	' ADCON1 is register hexadecimal 9f
Symbol  OPTION_REG = $81	' OPTION_REG is register hex 81

Poke OPTION_REG, 127	' Enable internal pullups on PORTB
Poke ADCON1, 7	' Set analog pins to digital
Poke TRISD, 0	' Set all PORTD lines to output
Poke TRISE, 0	' Set all PORTE lines to output
Poke PORTE, 0	' Start with enable low

Pause 200	' Wait for LCD to power up
GoSub lcdinit	' Initialize the lcd
GoSub lcdclr	' Clear lcd screen
	
loop:

	GoSub getkey			' Scan for keypress	
	IF key = $ff Then loop	' If no key, do it again
	
	GoSub lcddata	' Send letter in key(B2) to lcd

	GoTo loop	' Do it forever



' Subroutine to get a key from keypad
getkey:
	' Check for keypress
	b1 = 1
	For row = 0 TO 3        ' Loop for 4 rows in keypad
        Poke PORTB, 0       ' All output pins low

		b0 = b1 ^ $ff		' Prepare value with 0 in one bit
        Poke TRISB, b0		' Set one row pin to output
        
        Peek PORTB, b0		' Read column pins
        col = b0 / 16		' Shift right 4 places

        IF col <> $0F Then gotkey 	' Check if key is pressed
        
        b1 = b1 * 2			' Shift left one place
	Next row				' Repeat for the next row
	
	key = $FF				' No keys down, set key to $FF
	Return          		' Return to main loop
	
gotkey: 					' Change row and column to key number 0 - 15
	Pause 15                ' Debounce
	
	' Wait for all keys up
	
	Poke PORTB, 0           ' All output pins low
	Poke TRISB, $F0         ' Least significant 4 pins out, top 4 pins in
	Peek PORTB, b0			' Read column pins
	b0 = b0 / 16			' Shift right 4 places
	IF b0 <> $0F Then gotkey	' If any keys down, loop
	b0 = col^$0F			' Invert the column data
	b1 = 0					' Clear b1 for use as a loop counter
one_more:
	b1 = b1 + 1				' Count loop iterations (represents highest bit set)
	b0 = b0 / 2				' Shift b0 right on each loop
	IF b0 <> 0 Then one_more	' If b0 still has a logic high, try again

	b0 = row * 4			' Combine row and column into one number
	b0 = b0 + b1 - 1
	
	LookUp b0, ("123A456B789C*0#D"), key	' Convert key number into display value
	
Return			' Return to main loop

' Demonstrate operation of an LCD in 4-bit mode



' Subroutine to initialize the lcd - uses B2 and B3
lcdinit: Pause 15	' Wait at least 15ms

	Poke PORTD, $33	' Initialize the lcd
	GoSub lcdtog	' Toggle the lcd enable line

	Pause 5		' Wait at least 4.1ms

	Poke PORTD, $33	' Initialize the lcd
	GoSub lcdtog	' Toggle the lcd enable line

	Pause 1		' Wait at least 100us

	Poke PORTD, $33	' Initialize the lcd
	GoSub lcdtog	' Toggle the lcd enable line

	Pause 1		' Wait once more for good luck

	Poke PORTD, $22	' Put lcd into 4 bit mode
	GoSub lcdtog	' Toggle the lcd enable line

	B2 = $28	' 4 bit mode, 2 lines, 5x7 font
	GoSub lcdcom	' Send B2 to lcd

	B2 = $0c	' Lcd display on, no cursor, no blink
	GoSub lcdcom	' Send B2 to lcd

	B2 = $06	' Lcd entry mode set, increment, no shift
	GoTo lcdcom	' Exit through send lcd command

' Subroutine to clear the lcd screen - uses B2 and B3
lcdclr:	B2 = 1		' Set B2 to clear command and fall through to lcdcom

' Subroutine to send a command to the lcd - uses B2 and B3
lcdcom:	Poke PORTE, 0	' Set RS to command
lcdcd:	B3 = B2 & $f0	' Isolate top 4 bits
	Poke PORTD, B3	' Send upper 4 bits to lcd
	GoSub lcdtog	' Toggle the lcd enable line

	B3 = B2 * 16	' Shift botton 4 bits up to top 4 bits
	Poke PORTD, B3	' Send lower 4 bits to lcd
	GoSub lcdtog	' Toggle the lcd enable line

        Pause 2         ' Wait 2ms for write to complete
	Return

' Subroutine to send data to the lcd - uses B2 and B3
lcddata: Poke PORTE, 1	' Set RS to data
	GoTo lcdcd
 
' Subroutine to toggle the lcd enable line
lcdtog:	Peek PORTE, B0	' Get current PORTE value
	B0 = B0 | %00000010	' Set lcd enable line high
	Poke PORTE, B0
	B0 = B0 & %11111101	' Set lcd enable line low
	Poke PORTE, B0
	Return

End
